"""
数据分析模块的业务逻辑层
"""
from typing import List

from app.crud import analytics_crud, device_crud
from app.models.analytics_models import DeviceInventoryRow
from app.schemas.analytics_schemas import (
	ConsumptionStatsRequest, ConsumptionStatsResponse,
	AlarmStatsRequest, AlarmStatsResponse,
	DeviceInventoryRequest, DeviceInventoryResponse, AlarmStatsItem, AlarmTrendItem, AlarmTopDeviceItem,
	AnalyticsGroupBy, DeviceDailyConsumptionRequest, DeviceDailyConsumptionResponse, DailyConsumptionItem
)
from common.base_enums import MeterType, DeviceStatus, MeterPhase
from common.database import DatabaseService
from common.exceptions import BizException, ErrorCode
from common.logger import create_logger

logger = create_logger("analytics_service")


class AnalyticsService:
	"""数据分析服务类"""

	@staticmethod
	async def get_consumption_stats(
			db: DatabaseService,
			request: ConsumptionStatsRequest
	) -> ConsumptionStatsResponse:
		"""
		获取用量统计数据
		"""
		try:
			# 日期范围限制（最多31天）
			if (request.end_date - request.start_date).days > 31:
				raise BizException.from_error_code(
					ErrorCode.BAD_REQUEST, message="日期范围不能超过31天"
				)

			# 获取电表编码列表
			meter_codes = []
			if request.station_id:
				# 根据台区ID获取电表编码
				meter_codes = await analytics_crud.get_meter_codes_by_station(db, request.station_id)
			elif request.device_id:
				# 根据设备ID获取电表编码（这里简化处理，实际可能需要更复杂的逻辑）
				meter = await device_crud.get_meter_by_id(db, request.device_id)
				if meter:
					meter_codes = [meter.code]
			else:
				# 获取所有电表设备号（简化处理，实际应该考虑权限）
				try:
					rows = db.get_all("SELECT serial_no FROM dev_meter WHERE deleted = 0")
					meter_codes = [row["serial_no"] for row in rows]
				except BizException as e:
					logger.exception("获取所有电表编码失败: %s", e)
					raise BizException.from_error_code(ErrorCode.DB_QUERY_ERROR, message="获取电表数据失败") from e

			if not meter_codes:
				logger.info("未找到相关电表数据，返回默认统计值")
				return ConsumptionStatsResponse(
					total_kwh=0.0,
					a_phase_kwh=0.0,
					b_phase_kwh=0.0,
					c_phase_kwh=0.0,
					three_phase_kwh=0.0,
					avg_daily_kwh=0.0,
					max_demand_kw=0.0,
					max_demand_time=None
				)

			# 获取冻结电量数据
			freeze_data = await analytics_crud.get_meter_freeze_data(
				db, meter_codes, request.start_date, request.end_date
			)

			# 计算总用电量
			total_kwh = sum(item.total_kwh for item in freeze_data)

			# 计算日均用电量
			days = (request.end_date - request.start_date).days
			avg_daily_kwh = total_kwh / days if days > 0 else 0.0

			# 获取分相用电量（这里需要关联电表相别信息）
			a_phase_kwh = 0.0
			b_phase_kwh = 0.0
			c_phase_kwh = 0.0
			three_phase_kwh = 0.0

			# 如果有电表编码，获取各相的用电量
			if meter_codes:
				try:
					# 获取A相电表
					a_phase_meters = db.get_all("""
                        SELECT m.serial_no, f.total_kwh 
                        FROM dev_meter m 
                        LEFT JOIN dc_meter_freeze f ON m.serial_no = f.serial_no
                        WHERE m.serial_no IN ({}) AND m.phase = 'A' AND f.freeze_day BETWEEN %s AND %s
                    """.format(','.join(['%s'] * len(meter_codes))),
												meter_codes + [request.start_date.date(), request.end_date.date()])

					a_phase_kwh = sum(row["total_kwh"] for row in a_phase_meters)

					# 获取B相电表
					b_phase_meters = db.get_all("""
                        SELECT m.serial_no, f.total_kwh 
                        FROM dev_meter m 
                        JOIN dc_meter_freeze f ON m.serial_no = f.serial_no
                        WHERE m.serial_no IN ({}) AND m.phase = 'B' AND f.freeze_day BETWEEN %s AND %s
                    """.format(','.join(['%s'] * len(meter_codes))),
												meter_codes + [request.start_date.date(), request.end_date.date()])

					b_phase_kwh = sum(row["total_kwh"] for row in b_phase_meters)

					# 获取C相电表
					c_phase_meters = db.get_all("""
                        SELECT m.serial_no, f.total_kwh 
                        FROM dev_meter m 
                        JOIN dc_meter_freeze f ON m.serial_no = f.serial_no
                        WHERE m.serial_no IN ({}) AND m.phase = 'C' AND f.freeze_day BETWEEN %s AND %s
                    """.format(','.join(['%s'] * len(meter_codes))),
												meter_codes + [request.start_date.date(), request.end_date.date()])

					c_phase_kwh = sum(row["total_kwh"] for row in c_phase_meters)

					# 获取三相电表
					three_phase_meters = db.get_all("""
                        SELECT m.serial_no, f.total_kwh 
                        FROM dev_meter m 
                        JOIN dc_meter_freeze f ON m.serial_no = f.serial_no
                        WHERE m.serial_no IN ({}) AND m.meter_type = 'THREE_PHASE' AND f.freeze_day BETWEEN %s AND %s
                    """.format(','.join(['%s'] * len(meter_codes))),
													meter_codes + [request.start_date.date(), request.end_date.date()])

					three_phase_kwh = sum(row["total_kwh"] for row in three_phase_meters)
				except BizException as e:
					logger.warning(f"获取分相用电量失败: {e}，使用默认值")

			# 获取峰值功率
			max_demand_kw = 0.0
			max_demand_time = None
			try:
				max_power_data = await analytics_crud.get_max_power_data(
					db, meter_codes, request.start_date, request.end_date
				)
				if max_power_data and max_power_data[0]:
					max_demand_kw = max_power_data[0] / 1000 if max_power_data[0] else 0.0
					max_demand_time = max_power_data[1] if len(max_power_data) > 1 else None
			except BizException as e:
				logger.warning(f"获取峰值功率数据失败: {e}，使用默认值")

			logger.info(f"用量统计完成: total_kwh={total_kwh}, avg_daily_kwh={avg_daily_kwh}")
			return ConsumptionStatsResponse(
				total_kwh=total_kwh,
				a_phase_kwh=a_phase_kwh,
				b_phase_kwh=b_phase_kwh,
				c_phase_kwh=c_phase_kwh,
				three_phase_kwh=three_phase_kwh,
				avg_daily_kwh=avg_daily_kwh,
				max_demand_kw=max_demand_kw,
				max_demand_time=max_demand_time
			)

		except BizException as e:
			logger.warning("用量统计业务异常: %s", e.message)
			raise e

	@staticmethod
	async def get_alarm_stats(
			db: DatabaseService,
			request: AlarmStatsRequest
	) -> AlarmStatsResponse:
		"""
		获取告警事件统计数据
		"""
		try:
			response = AlarmStatsResponse(by_status=[], by_type=[], trend=[], top_devices=[])

			# 按状态统计
			try:
				if request.group_by in [AnalyticsGroupBy.ALL, None, AnalyticsGroupBy.STATUS]:
					status_stats = await analytics_crud.get_alarm_stats_by_status(
						db, request.start_date, request.end_date
					)
					response.by_status = [
						AlarmStatsItem(key=stat.status or "未知", count=stat.count)
						for stat in status_stats
					]
			except BizException as e:
				logger.warning(f"按状态统计告警失败: {e}")

			# 按类型统计
			try:
				if request.group_by in [AnalyticsGroupBy.ALL, None, AnalyticsGroupBy.TYPE]:
					type_stats = await analytics_crud.get_alarm_stats_by_type(
						db, request.start_date, request.end_date
					)
					response.by_type = [
						AlarmStatsItem(key=stat.alarm_type or "未知", count=stat.count)
						for stat in type_stats
					]
			except BizException as e:
				logger.warning(f"按类型统计告警失败: {e}")

			# 按天趋势
			try:
				if request.group_by in [AnalyticsGroupBy.ALL, None, AnalyticsGroupBy.DAY]:
					trend_stats = await analytics_crud.get_alarm_trend_stats(
						db, request.start_date, request.end_date
					)
					response.trend = [
						AlarmTrendItem(date=str(stat.raised_at), count=stat.count)
						for stat in trend_stats
					]
			except BizException as e:
				logger.warning(f"按天统计告警趋势失败: {e}")

			# 设备排名
			try:
				if request.group_by in [AnalyticsGroupBy.ALL, None, AnalyticsGroupBy.DEVICE]:
					top_devices = await analytics_crud.get_top_alarm_devices(
						db, request.start_date, request.end_date
					)
					response.top_devices = [
						AlarmTopDeviceItem(meter_code=stat.meter_code or "未知", count=stat.count)
						for stat in top_devices
					]
			except BizException as e:
				logger.warning(f"获取告警设备排名失败: {e}")

			logger.info("告警统计完成")
			return response

		except BizException as e:
			logger.exception("告警统计服务异常: %s", e)
			raise BizException.from_error_code(ErrorCode.INTERNAL_SERVER_ERROR,
											   message=f"告警统计失败: {str(e)}") from e

	@staticmethod
	async def get_device_inventory_stats(
			db: DatabaseService,
			request: DeviceInventoryRequest
	) -> DeviceInventoryResponse:
		"""
		区域设备资产统计：CRUD 取扁平行 → 聚合成对外 DTO
		"""
		rows: List[DeviceInventoryRow] = await analytics_crud.get_device_inventory_stats(
			db,
			request.province_code,
			request.city_code,
			request.area_code
		)

		# 一次性聚合
		resp = DeviceInventoryResponse().model_construct()
		for r in rows:
			# 总数维度（SQL 已聚合，任一行即可）
			if r.total_stations is not None:
				resp.total_stations = r.total_stations
			if r.total_boxes is not None:
				resp.total_boxes = r.total_boxes
			if r.total_rtus is not None:
				resp.total_rtus = r.total_rtus
			if r.total_meters is not None:
				resp.total_meters = r.total_meters

			# 分相维度
			if r.phase == MeterPhase.A.value:
				resp.a_phase_meters += r.count
			elif r.phase == MeterPhase.B.value:
				resp.b_phase_meters += r.count
			elif r.phase == MeterPhase.C.value:
				resp.c_phase_meters += r.count
			elif r.phase == MeterType.THREE_PHASE.value:
				resp.three_phase_meters += r.count

			# 状态维度
			if r.status == DeviceStatus.ONLINE.value:
				resp.online_count += r.count
			elif r.status == DeviceStatus.OFFLINE.value:
				resp.offline_count += r.count
			elif r.status == DeviceStatus.ABNORMAL.value:
				resp.abnormal_count += r.count

		logger.info("设备资产统计完成: %s", resp)
		return resp

	@staticmethod
	async def get_device_daily_consumption(
			db: DatabaseService,
			request: DeviceDailyConsumptionRequest
	) -> DeviceDailyConsumptionResponse:
		"""
		获取设备每日用电量统计数据
		
		参数:
		- request: 设备用电量统计请求参数
		
		返回:
		- DeviceDailyConsumptionResponse: 设备每日用电量统计结果
		"""
		try:
			# 查询用电量数据
			try:
				rows = await analytics_crud.get_device_daily_consumption(
					db, request.serial_no, request.station_id, request.start_time, request.end_time
				)
			except Exception as e:
				logger.exception("查询设备用电量数据失败: %s", e)
				raise BizException.from_error_code(
					ErrorCode.DB_QUERY_ERROR, message=f"查询设备用电量数据失败: {str(e)}"
				) from e

			# 如果没有数据，返回默认值
			if not rows:
				if request.serial_no:
					logger.info("未找到设备 %s 的用电量数据", request.serial_no)
				else:
					logger.info("未找到台区 %s 的用电量数据", request.station_id)
				
				return DeviceDailyConsumptionResponse(
					serial_no=request.serial_no,
					station_id=request.station_id,
					total_days=0,
					total_consumption=0.0,
					avg_daily_consumption=0.0,
					daily_data=[]
				)

			# 数据处理和统计
			daily_data = []
			total_consumption = 0.0

			for row in rows:
				freeze_day_str = str(row["freeze_day"])
				total_kwh = float(row["total_kwh"]) if row["total_kwh"] is not None else 0.0

				daily_data.append(DailyConsumptionItem(
					freeze_day=freeze_day_str,
					total_kwh=total_kwh
				))
				total_consumption += total_kwh

			# 计算统计值
			total_days = len(daily_data)
			avg_daily_consumption = total_consumption / total_days if total_days > 0 else 0.0

			if request.serial_no:
				logger.info(
					"设备用电量统计完成: serial_no=%s, total_days=%d, total_consumption=%.2f",
					request.serial_no, total_days, total_consumption
				)
			else:
				logger.info(
					"台区用电量统计完成: station_id=%s, total_days=%d, total_consumption=%.2f",
					request.station_id, total_days, total_consumption
				)

			return DeviceDailyConsumptionResponse(
				serial_no=request.serial_no,
				station_id=request.station_id,
				total_days=total_days,
				total_consumption=total_consumption,
				avg_daily_consumption=avg_daily_consumption,
				daily_data=daily_data
			)

		except BizException as e:
			logger.warning("设备用电量统计业务异常: %s", e.message)
			raise e
		except Exception as e:
			logger.exception("设备用电量统计系统异常: %s", e)
			raise BizException.from_error_code(
				ErrorCode.INTERNAL_SERVER_ERROR,
				message=f"设备用电量统计失败: {str(e)}"
			) from e
